package com.yunhe.common.util;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.alibaba.fastjson.util.ParameterizedTypeImpl;
import com.yunhe.common.constant.CacheConsts;
import com.yunhe.common.model.data.Measurement;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.Pipeline;

import java.lang.reflect.Type;
import java.util.*;

@Component
public class RedisClient {
    @Autowired
    private JedisPool jedisPool;

    public List<String> mget(List<String> keys) {
        String[] arr = new String[keys.size()];
        keys.toArray(arr);
        return mget(arr);
    }

    public List<String> mget(String[] keys) {
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            return jedis.mget(keys);
        } finally {
            if (jedis != null) {
                jedis.close();
            }
        }
    }

    /**
     * 存入对象（不过期）
     * @param key 键
     * @param object 对象
     */
    public void setObjectForever(String key, Object object) {
        String json = JSON.toJSONString(object);
        set(key, json);
    }

    /**
     * 存入对象（默认存活时间）
     * @param key 键
     * @param object 对象
     */
    public void setObject(String key, Object object) {
        setObject(key, object, CacheConsts.getDefaultTime());
    }

    /**
     * 存入对象（指定存活时间）
     * @param key 键
     * @param object 对象
     * @param liveTime 存活时间
     */
    public void setObject(String key, Object object, int liveTime) {
        String json = JSON.toJSONString(object);
        set(key, json, liveTime);
    }

    /**
     * 获取对象
     * @param key 键
     * @param type 对象类型
     */
    public <T> T getObject(String key, Class<T> type) {
        String json = get(key);
        if (!StringUtil.isEmpty(json)) {
            return JSONObject.parseObject(json, type);
        } else {
            return null;
        }
    }

    /**
     * 获取对象
     * @param key 键
     * @param type 对象类型
     */
    public <T> T getObject(String key, TypeReference<T> type) {
        String json = get(key);
        if (!StringUtil.isEmpty(json)) {
            return JSONObject.parseObject(json, type);
        } else {
            return null;
        }
    }

    /**
     * 获取指定对象类型的List
     * @param key 键
     * @param type 对象类型
     */
    public <E> List<E> getAsList(String key, Class<E> type) {
        String json = get(key);
        if (!StringUtil.isEmpty(json)) {
            return JSONObject.parseArray(json, type);
        } else {
            return null;
        }
    }

    /**
     * 获取指定对象类型的Map
     * @param key 键
     * @param kClass Map键类型
     * @param vClass Map值类型
     */
    public <K, V> Map<K, V> getAsMap(String key, Class<K> kClass, Class<V> vClass) {
        String json = get(key);
        Type[] actualTypeArguments = {kClass ,vClass};
        Type type = new ParameterizedTypeImpl(actualTypeArguments, null, Map.class);
        if (!StringUtil.isEmpty(json)) {
            return JSONObject.parseObject(json, type);
        } else {
            return null;
        }
    }

    /**
     * 移除对象
     * @param key 键
     */
    public void delObject(String key) {
        del(key);
    }

    /**
     * 批量添加量测key value
     * @param valus
     */
    public void setMeasurementDouble(Map<Long, Double> valus) {
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            Pipeline pip = jedis.pipelined();
            for (Map.Entry<Long, Double> entry : valus.entrySet()) {
                pip.set(String.valueOf(entry.getKey()), String.valueOf(entry.getValue()));
            }
            pip.sync();
        } finally {
            if (jedis != null ) jedis.close();
        }
    }

    /**
     * 添加量测key value，value是对象
     * @param value
     */
    public void setMeasurementObject(Measurement value) {
        //如果val为空则直接删除
        if(value.getVal() == null) {
            del(CacheConsts.KEY.MEASURE_OBJ.value(value.getPointNumber()));
        } else {
            String json = JSON.toJSONString(value);
            set(CacheConsts.KEY.MEASURE_OBJ.value(value.getPointNumber()), json);
        }
    }

    /**
     * 批量添加key value，value是对象
     * @param valus
     */
    public void setMeasurementObject(List<Measurement> valus) {
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            Pipeline pip = jedis.pipelined();
            for (Measurement entry : valus) {
                //如果val为空则直接删除
                if(entry.getVal() == null) {
                    pip.del(CacheConsts.KEY.MEASURE_OBJ.value(entry.getPointNumber()));
                } else {
                    String json = JSON.toJSONString(entry);
                    pip.set(CacheConsts.KEY.MEASURE_OBJ.value(entry.getPointNumber()), json);
                }
            }
            pip.sync();
        } finally {
            if (jedis != null ) jedis.close();
        }
    }

    /**
     * 获取量测redis value
     * @param pnList
     * @return
     */
    public Map<Long, Double> getMeasurementDouble(List<Long> pnList) {
        if (pnList == null) {
            return null;
        }
        Jedis jedis = null;
        Map<Long, Double> resultMap = new HashMap<>(pnList.size());
        try {
            jedis = jedisPool.getResource();
            List<String> points = new ArrayList<>();
            pnList.forEach(v -> points.add(CacheConsts.KEY.MEASURE_OBJ.value(v)));
            String[] keys = new String[points.size()];
            points.toArray(keys);
            List<String> values = jedis.mget(keys);
            for (int i = 0; i < pnList.size(); i++) {
                String value = values.get(i);
                if (!StringUtil.isEmpty(value)) {
                    //平台2.6从pointNumber的key改为measureObj_***
                    Measurement measurement = JSONObject.parseObject(value, Measurement.class);
                    if (measurement != null) {
                        resultMap.put(pnList.get(i), measurement.getVal());
                    } else {
                        resultMap.put(pnList.get(i), null);
                    }
                } else {
                    resultMap.put(pnList.get(i), null);
                }
            }
        } finally {
            if (jedis != null ) jedis.close();
        }
        return resultMap;
    }

    /**
     * 获取量测redis value
     * @param pnList
     * @return
     */
    public Map<String, Double> getMeasurementDoubleByStr(List<String> pnList) {
        if (pnList == null) {
            return null;
        }
        Jedis jedis = null;
        Map<String, Double> resultMap = new HashMap<>(pnList.size());
        try {
            jedis = jedisPool.getResource();
            List<String> points = new ArrayList<>();
            pnList.forEach(v -> points.add(CacheConsts.KEY.MEASURE_OBJ.value(v)));
            String[] keys = new String[points.size()];
            points.toArray(keys);
            List<String> values = jedis.mget(keys);
            for (int i = 0; i < pnList.size(); i++) {
                String value = values.get(i);
                if (!StringUtil.isEmpty(value)) {
                    //平台2.6从pointNumber的key改为measureObj_***
                    Measurement measurement = JSONObject.parseObject(value, Measurement.class);
                    if (measurement != null) {
                        resultMap.put(pnList.get(i), measurement.getVal());
                    } else {
                        resultMap.put(pnList.get(i), null);
                    }
                } else {
                    resultMap.put(pnList.get(i), null);
                }
            }
        } finally {
            if (jedis != null ) jedis.close();
        }
        return resultMap;
    }

    /**
     * 获取量测redis value
     * @param pointNumber 点号
     * @return
     */
    public Measurement getMeasurementObj(Long pointNumber) {
        Jedis jedis = null;
        Measurement measurement =null;
        try {
            jedis = jedisPool.getResource();
            String value = jedis.get(CacheConsts.KEY.MEASURE_OBJ.value(pointNumber));
            if (!StringUtil.isEmpty(value) && !"null".equals(value)) {
                measurement = JSONObject.parseObject(value, Measurement.class);
            }
        } finally {
            if (jedis != null ) jedis.close();
        }
        return measurement;
    }

    /**
     * 获取量测redis value
     * @param pnList
     * @return
     */
    public Map<Long, Measurement> getMeasurementObject(List<Long> pnList) {
        Jedis jedis = null;
        Map<Long, Measurement> resultMap = new HashMap<>();
        try {
            jedis = jedisPool.getResource();
            for (Long pointNumber : pnList) {
                String value = jedis.get(CacheConsts.KEY.MEASURE_OBJ.value(pointNumber));
                if (!StringUtil.isEmpty(value) && !"null".equals(value)) {
                    resultMap.put(pointNumber, JSONObject.parseObject(value, Measurement.class));
                }
            }
        } finally {
            if (jedis != null ) jedis.close();
        }
        return resultMap;
    }

    /**
     * 获取量测redis value
     * @param pnList
     * @return
     */
    public Map<String, Measurement> getMeasurementObjectByStr(List<String> pnList) {
        Jedis jedis = null;
        Map<String, Measurement> resultMap = new HashMap<>();
        try {
            jedis = jedisPool.getResource();
            for (String pointNumber : pnList) {
                String value = jedis.get(CacheConsts.KEY.MEASURE_OBJ.value(pointNumber));
                if (!StringUtil.isEmpty(value) && !"null".equals(value)) {
                    resultMap.put(pointNumber, JSONObject.parseObject(value, Measurement.class));
                }
            }
        } finally {
            if (jedis != null ) jedis.close();
        }
        return resultMap;
    }

    /**
     * 通过key删除（字节）
     * @param key
     */
    public void del(byte[] key) {
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            jedis.del(key);
        } finally {
            if (jedis != null ) jedis.close();
        }
    }
    /**
     * 通过key删除
     * @param key
     */
    public void del(String key) {
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            jedis.del(key);
        } finally {
            if (jedis != null ) jedis.close();
        }
    }

    /**
     * 批量删除key
     * @param keys
     */
    public void del(List<String> keys) {
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            Pipeline pip = jedis.pipelined();
            for (String key : keys) {
                pip.del(key);
            }
            pip.sync();
        } finally {
            if (jedis != null ) jedis.close();
        }
    }

    /**
     * 批量重命名key
     * @param keys
     */
    public void rename(List<String> keys, List<String> newKeys) {
        if (keys == null || newKeys == null || keys.size() != newKeys.size()) {
            return;
        }
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            Pipeline pip = jedis.pipelined();
            for (int i = 0; i < keys.size(); i++) {
                pip.rename(keys.get(i), newKeys.get(i));
            }
            pip.sync();
        } finally {
            if (jedis != null ) jedis.close();
        }
    }

    /**
     * 添加key value 并且设置存活时间(byte)
     * @param key
     * @param value
     * @param liveTime
     */
    public void set(byte[] key, byte[] value, int liveTime) {
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            jedis.set(key, value);
            jedis.expire(key, liveTime);
        } finally {
            if (jedis != null ) jedis.close();
        }
    }

    /**
     * 添加key value 并且设置存活时间
     * @param key
     * @param value
     * @param liveTime
     */
    public void set(String key, String value, int liveTime) {
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            jedis.set(key, value);
            jedis.expire(key, liveTime);
        } finally {
            if (jedis != null ) jedis.close();
        }
    }

    /**
     * 批量添加key value
     * @param valus
     */
    public void set(Map<String, String> valus) {
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            Pipeline pip = jedis.pipelined();
            for (Map.Entry<String, String> entry : valus.entrySet()) {
                pip.set(entry.getKey(), entry.getValue());
            }
            pip.sync();
        } finally {
            if (jedis != null ) jedis.close();
        }
    }

    /**
     * 添加key value 并且设置存活时间
     * @param key
     * @param value

     */
    public void hset(String key, String field, String value) {
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            jedis.hset(key, field, value);
        } finally {
            if (jedis != null ) jedis.close();
        }
    }

    public void hmset(String key, Map<String, String> map) {
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            jedis.hmset(key, map);
        } finally {
            if (jedis != null ) {
                jedis.close();
            }
        }
    }

    public void hmset(String key, Map<String, String> map, int liveTime) {
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            jedis.hmset(key, map);
            jedis.expire(key, liveTime);
        } finally {
            if (jedis != null ) {
                jedis.close();
            }
        }
    }

    /**
     * 添加key value
     * @param key
     * @param value
     */
    public void set(String key, String value) {
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            jedis.set(key, value);
        } finally {
            if (jedis != null ) jedis.close();
        }
    }
    /**添加key value (字节)(序列化)
     * @param key
     * @param value
     */
    public void set(byte[] key, byte[] value) {
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            jedis.set(key, value);
        } finally {
            if (jedis != null ) jedis.close();
        }
    }
    /**
     * 获取redis value (String)
     * @param key
     * @return
     */
    public String get(String key) {
        Jedis jedis = null;
        String value;
        try {
            jedis = jedisPool.getResource();
            value = jedis.get(key);
        } finally {
            if (jedis != null ) jedis.close();
        }
        return value;
    }
    /**
     * 获取redis value (String)
     * @param key
     * @return
     */
    public Map<String, String> hgetAll(String key) {
        Jedis jedis = null;
        Map<String, String> value;
        try {
            jedis = jedisPool.getResource();
            value = jedis.hgetAll(key);
        } finally {
            if (jedis != null ) jedis.close();
        }
        return value;
    }

    /**
     * 获取redis value (String)
     * @param key
     * @return
     */
    public String hget(String key, String field) {
        Jedis jedis = null;
        String value;
        try {
            jedis = jedisPool.getResource();
            value = jedis.hget(key,field);
        } finally {
            if (jedis != null ) jedis.close();
        }
        return value;
    }

    public List<String> hmget(String key, String[] fields) {
        Jedis jedis = null;
        List<String> values;
        try {
            jedis = jedisPool.getResource();
            values = jedis.hmget(key, fields);
        } finally {
            if (jedis != null ) {
                jedis.close();
            }
        }
        return values;
    }

    public String hmget(String key, String field) {
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            List<String> list = jedis.hmget(key, field);
            if (list != null && list.size() > 0) {
                return list.get(0);
            } else {
                return null;
            }
        } finally {
            if (jedis != null ) {
                jedis.close();
            }
        }
    }

    /**
     * 获取redis value (byte[] )(反序列化)
     * @param key
     * @return
     */
    public byte[] get(byte[] key) {
        Jedis jedis = null;
        byte[] value;
        try {
            jedis = jedisPool.getResource();
            value = jedis.get(key);
        } finally {
            if (jedis != null ) jedis.close();
        }
        return value;
    }

    /**
     * 通过正则匹配keys
     * @param pattern
     * @return
     */
    public Set<String> keys(String pattern) {
        Jedis jedis = null;
        Set<String> value;
        try {
            jedis = jedisPool.getResource();
            value = jedis.keys(pattern);
        } finally {
            if (jedis != null ) jedis.close();
        }
        return value;
    }

    /**
     * 检查key是否已经存在
     * @param key
     * @return
     */
    public boolean exists(String key) {
        Jedis jedis = null;
        boolean value;
        try {
            jedis = jedisPool.getResource();
            value = jedis.exists(key);
        } finally {
            if (jedis != null ) jedis.close();
        }
        return value;
    }

    /*******************redis list操作************************/
    /**
     * 往list中添加元素
     * @param key
     * @param value
     */
    public void lpush(String key, String value) {
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            jedis.lpush(key, value);
        } finally {
            if (jedis != null ) jedis.close();
        }
    }

    public void rpush(String key, String value) {
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            jedis.rpush(key, value);
        } finally {
            if (jedis != null ) jedis.close();
        }
    }

    /**
     * 数组长度
     * @param key
     * @return
     */
    public Long llen(String key) {
        Jedis jedis = null;
        Long len;
        try {
            jedis = jedisPool.getResource();
            len = jedis.llen(key);
        } finally {
            if (jedis != null ) jedis.close();
        }
        return len;
    }

    /**
     * 获取下标为index的value
     * @param key
     * @param index
     * @return
     */
    public String lindex(String key, Long index) {
        Jedis jedis = null;
        String str;
        try {
            jedis = jedisPool.getResource();
            str = jedis.lindex(key, index);
        } finally {
            if (jedis != null ) jedis.close();
        }
        return str;
    }

    public String lpop(String key) {
        Jedis jedis = null;
        String str;
        try {
            jedis = jedisPool.getResource();
            str = jedis.lpop(key);
        } finally {
            if (jedis != null ) jedis.close();
        }
        return str;
    }

    public List<String> lrange(String key, long start, long end) {
        Jedis jedis = null;
        List<String> str;
        try {
            jedis = jedisPool.getResource();
            str = jedis.lrange(key, start, end);
        } finally {
            if (jedis != null ) jedis.close();
        }
        return str;
    }
    /*********************redis list操作结束**************************/

    /*********************redis ZSET操作开始**************************/

    public Long zadd(String key, double score, String value) {
        Jedis jedis = null;
        Long str;
        try {
            jedis = jedisPool.getResource();
            str = jedis.zadd(key, score, value);
        } finally {
            if (jedis != null ) jedis.close();
        }
        return str;
    }

    public Long zrem(String key, String value) {
        Jedis jedis = null;
        Long str;
        try {
            jedis = jedisPool.getResource();
            str = jedis.zrem(key, value);
        } finally {
            if (jedis != null ) jedis.close();
        }
        return str;
    }

    public Set<String> zrangeByScore(String key, double start, double end) {
        Jedis jedis = null;
        Set<String> str;
        try {
            jedis = jedisPool.getResource();
            str = jedis.zrangeByScore(key, start, end);
        } finally {
            if (jedis != null ) jedis.close();
        }
        return str;
    }
    /*********************redis ZSET操作结束**************************/

    /**
     * 清空redis 所有数据
     * @return
     */
    public String flushDB() {
        Jedis jedis = null;
        String str;
        try {
            jedis = jedisPool.getResource();
            str = jedis.flushDB();
        } finally {
            if (jedis != null ) jedis.close();
        }
        return str;
    }
    /**
     * 查看redis里有多少数据
     */
    public long dbSize() {
        Jedis jedis = null;
        long len;
        try {
            jedis = jedisPool.getResource();
            len = jedis.dbSize();
        } finally {
            if (jedis != null ) jedis.close();
        }
        return len;
    }
    /**
     * 检查是否连接成功
     * @return
     */
    public String ping() {
        Jedis jedis = null;
        String str;
        try {
            jedis = jedisPool.getResource();
            str = jedis.ping();
        } finally {
            if (jedis != null ) jedis.close();
        }
        return str;
    }

    /**
     * 获取分布式锁
     * @param lockKey 锁
     * @param value 锁标志
     * @param time 过期时间
     */
    public Boolean tryGetDistributedLock(String lockKey, String value, int time) {
        Jedis jedis = null;
        String str;
        try {
            jedis = jedisPool.getResource();
            str = jedis.set(lockKey, value, "nx", "ex", time);
        } finally {
            if (jedis != null ) {
                jedis.close();
            }
        }
        return "OK".equals(str);
    }

    /**
     * 释放分布式锁
     * @param lockKey 锁
     * @param value 锁标识
     */
    public Boolean releaseDistributedLock(String lockKey, String value) {
        Jedis jedis = null;
        Long success = 1L;
        Object result;
        try {
            jedis = jedisPool.getResource();
            String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
            result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(value));
        } finally {
            if (jedis != null ) {
                jedis.close();
            }
        }
        return success.equals(result);
    }

    /**
     * 查看key的有效期（键到期的剩余时间(秒)）
     * @param key 键
     */
    public Long getLiveTime(String key) {
        Jedis jedis = null;
        Long liveTime = 0L;
        try {
            jedis = jedisPool.getResource();
            liveTime = jedis.ttl(key);
        } finally {
            if (jedis != null ) jedis.close();
        }
        return liveTime;
    }

    /**
     * value自增+1
     * @param key
     * @return
     */
    public Long incr(String key) {
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            return jedis.incr(key);
        } finally {
            if (jedis != null ) {
                jedis.close();
            }
        }
    }
}
